package org.example.memora.service.serviceImpl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.example.memora.DTO.PostCreateDTO;
import org.example.memora.DTO.PostDTO;
import org.example.memora.DTO.UserRedisDTO;
import org.example.memora.common.constant.RedisConstants;
import org.example.memora.common.utils.UserHolder;
import org.example.memora.entity.*;
import org.example.memora.entity.vo.PostVO;
import org.example.memora.mapper.*;
import org.example.memora.service.HashTagService;
import org.example.memora.service.PostService;
import org.hibernate.validator.internal.util.logging.Log_$logger;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.util.StringUtils;
import cn.hutool.core.util.IdUtil;
import java.time.LocalDateTime;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.example.memora.common.constant.RedisConstants.POST_KEY_PREFIX;

import org.example.memora.Repository.PostRepository;
import org.example.memora.common.result.Result;
import org.example.memora.entity.Post;
import org.example.memora.mapper.PostCollectStatMapper;
import org.example.memora.mapper.PostCommentStatMapper;
import org.example.memora.mapper.PostLikeStatMapper;
import org.example.memora.service.CategoryService;
import org.example.memora.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class PostServiceImpl implements PostService {
    private final  RestHighLevelClient restHighLevelClient;  // 在 ElasticsearchConfig 中已定义为 Bean
    private final  PostMapper postMapper;
    private final  UserMapper userMapper;
    private final PostViewLogMapper postViewLogMapper;
    private final UserInterestsMapper userInterestsMapper;
    private final PostCategoryMapper postCategoryMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final PostHashtagMapper postHashtagMapper;
     private final HashtagMapper hashtagMapper;
    @Override
    public Page<Post> getRecommendedPosts(String userId, Integer page, Integer size, String categoryId) {
        // 1. 将 userId 转换为 Integer
        Integer uid = null;
        try {
            uid = Integer.valueOf(userId);
        } catch (Exception e) {
            return new Page<>(page, size);
        }

        // 2. 获取用户已浏览的帖子 ID 列表
        String userViewedPostsKey = "user:" + userId + ":viewedPosts";  // 用户浏览记录 Redis key
        Set<String> viewedPostIds = stringRedisTemplate.opsForSet().members(userViewedPostsKey);  // 获取用户已浏览的帖子 ID 集合

        // 3. 从 Redis 获取热点帖子的 ID
        String hotPostsKey = "hot_posts";
        int redisFetchCount = size * 2;  // 获取的热点帖数量是页面大小的两倍
        Set<String> hotPostIdStrings = stringRedisTemplate.opsForZSet().reverseRange(hotPostsKey, 0, redisFetchCount - 1);
        List<String> hotPostIds = new ArrayList<>();
        if (hotPostIdStrings != null && !hotPostIdStrings.isEmpty()) {
            hotPostIds = hotPostIdStrings.stream().collect(Collectors.toList());
        }

        // 4. 获取新鲜帖子的 ID
        List<String> freshPostIds = stringRedisTemplate
                .opsForList()
                .range(RedisConstants.FRESH_POSTS_KEY, 0, size * 2 - 1);
        if (freshPostIds == null || freshPostIds.isEmpty()) {
            // 回退：如果 Redis 中没有（或刚好被清空），就从数据库拉一批
            freshPostIds = postMapper.selectLatestPostIds(size * 2);
        }
        // 5. 获取用户兴趣相关的帖子 ID
        List<String> interestPostIds = new ArrayList<>();
        List<Integer> interestIds = userInterestsMapper.selectInterestIdsByUserId(uid);
        if (interestIds != null && !interestIds.isEmpty()) {
            String interestsStr = interestIds.stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            String subQuery = "SELECT post_id FROM post_interests WHERE interest_id IN (" + interestsStr + ")";
            interestPostIds = postMapper.selectPostIdsBySubQuery(subQuery);  // 获取兴趣相关帖子ID
        }

        // 6. 合并所有帖子 ID 并去重
        Set<String> combinedPostIds = new LinkedHashSet<>();
        combinedPostIds.addAll(hotPostIds);
        combinedPostIds.addAll(interestPostIds);
        combinedPostIds.addAll(freshPostIds);

        // 7. 排除用户已浏览的帖子
        if (viewedPostIds != null && !viewedPostIds.isEmpty()) {
            Set<String> viewedPostIdSet = viewedPostIds.stream()
                    .collect(Collectors.toSet());
            combinedPostIds.removeAll(viewedPostIdSet);  // 排除已浏览的帖子 ID
        }

        // 8. 如果最终帖子不足，回退到数据库查询更多
        if (combinedPostIds.size() < size) {
            // 补充不足的帖子 ID，最多补充 size 个
            List<String> additionalPostIds = postMapper.selectLatestPostIds(size - combinedPostIds.size());
            combinedPostIds.addAll(additionalPostIds);
        }

        // 9. 根据去重后的帖子 ID 查询帖子详细信息
        List<Post> combinedPosts = new ArrayList<>();
        if (!combinedPostIds.isEmpty()) {
            if (StrUtil.isNotEmpty(categoryId)) {
                try {
                    Integer catId = Integer.valueOf(categoryId);
                    // 先过滤一轮
                    List<String> filteredPostIds = postMapper.selectPostIdsByCategory(catId, new ArrayList<>(combinedPostIds));
                    combinedPostIds = new LinkedHashSet<>(filteredPostIds);

                    // 补充不足
                    if (combinedPostIds.size() < size) {
                        List<String> additionalPostIds = postMapper.selectLatestPostIdsByCategory(catId, size+  combinedPostIds.size() );
                        combinedPostIds.addAll(additionalPostIds);

                    }
                } catch (NumberFormatException e) {
                    // 忽略分类条件错误
                }
            }

            // !!! 必须在 combinedPostIds 最终确定之后，再重新构建 queryWrapper
            if (!combinedPostIds.isEmpty()) {
                LambdaQueryWrapper<Post> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.in(Post::getPostId, combinedPostIds)
                        .eq(Post::getIsDeleted, 0)
                        .eq(Post::getStatus, 1);

                if (StrUtil.isNotEmpty(categoryId)) {
                    try {
                        queryWrapper.eq(Post::getCategoryId, Integer.valueOf(categoryId));
                    } catch (NumberFormatException e) {
                        // 忽略分类条件错误
                    }
                }

                combinedPosts = postMapper.selectList(queryWrapper);
            }
        }
        // 10. 将 List<Post> 转换为 List<PostDTO>
        List<PostDTO> postDTOList = combinedPosts.stream()
                .map(PostDTO::new)
                .collect(Collectors.toList());

        // 11. 计算综合分数（热度 + 新鲜度）
        double heatWeight = 0.7;  // 热度权重
        double freshnessWeight = 0.3;  // 新鲜度权重
        for (PostDTO postDTO : postDTOList) {
            postDTO.calculateScore(heatWeight, freshnessWeight);
        }

        // 12. 按综合分数排序
        postDTOList.sort((p1, p2) -> Double.compare(p2.getScore(), p1.getScore()));

        // 13. 手动分页
        int total = combinedPosts.size();
        int fromIndex = Math.min((page - 1) * size, total);
        int toIndex = Math.min(fromIndex + size, total);
        List<PostDTO> pageRecords = postDTOList.subList(fromIndex, toIndex);

        // 14. 将 PostDTO 转换回 Post
        List<Post> resultPosts = pageRecords.stream()
                .map(postDTO -> {
                    Post post = new Post();
                    // 从 PostDTO 中提取数据设置到 Post 实例中
                    post.setPostId(postDTO.getPostId());
                    post.setUserId(postDTO.getUserId());
                    post.setTitle(postDTO.getTitle());
                    post.setContent(postDTO.getContent());
                    post.setCoverUrl(postDTO.getCoverUrl());
                    post.setMediaUrls(postDTO.getMediaUrls());
                    post.setSummary(postDTO.getSummary());
                    post.setCategoryId(postDTO.getCategoryId());
                    post.setStatus(postDTO.getStatus());
                    post.setNoteType(postDTO.getNoteType());
                    post.setLikeCount(postDTO.getLikeCount());
                    post.setCollectionCount(postDTO.getCollectionCount());
                    post.setCommentCount(postDTO.getCommentCount());
                    post.setViewCount(postDTO.getViewCount());
                    post.setPinned(postDTO.getPinned());
                    post.setCount(postDTO.getCount());
                    post.setCreatedAt(postDTO.getCreatedAt());
                    post.setUpdatedAt(postDTO.getUpdatedAt());
                    post.setIsDeleted(postDTO.getIsDeleted());
                    return post;
                })
                .collect(Collectors.toList());

        // 15. 构建分页结果并返回
        Page<Post> postPage = new Page<>(page, size);
        postPage.setTotal(total);
        postPage.setRecords(resultPosts);
        return postPage;
    }

    @Override
    public Page<Post> searchPosts(String keyword,  String hashtag, Integer page, Integer size) {
        Page<Post> postPage = new Page<>(page, size);
        LambdaQueryWrapper<Post> queryWrapper = new LambdaQueryWrapper<>();

        // 1. 如果 keyword 不为空，则认为输入的是关键词，可能匹配标题、正文或用户名
        if (StrUtil.isNotEmpty(keyword)) {
                // ES 搜索标题或内容匹配关键词
                List<Post> esPosts = new ArrayList<>();
//                try {
//                    SearchRequest searchRequest = new SearchRequest("posts"); // 索引名称
//                    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//                    // 用multiMatch匹配标题和正文
//                    sourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, "title", "content"));
//                    // 分页设置：ES页码从0开始
//                    sourceBuilder.from((page - 1) * size);
//                    sourceBuilder.size(size);
//                    searchRequest.source(sourceBuilder);
//
//                    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//                    for (SearchHit hit : searchResponse.getHits().getHits()) {
//                        Post post = JSON.parseObject(hit.getSourceAsString(), Post.class);
//                        esPosts.add(post);
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    // 如果 ES 查询失败，可考虑退回到MySQL查询
//                }
                // 如果 ES 没名中，退回到MySQL查询
                if (esPosts.isEmpty()) {
                    queryWrapper.like(Post::getTitle, keyword)
                            .or()
                            .like(Post::getContent, keyword);
                }
                List<Post> mysqlPosts = postMapper.selectList(queryWrapper);
                // 检查关键词是否正好匹配用户名，若有则用MySQL查询该用户的帖子
                List<Post> mysqlPostsFromUsername = new ArrayList<>();
                User user = userMapper.findByUsername(keyword);
                if (user != null) {
                    queryWrapper.eq(Post::getUserId, user.getUserId());
                    // 例如只查询未删除的帖子
                    queryWrapper.eq(Post::getIsDeleted, 0);
                    mysqlPostsFromUsername = postMapper.selectList(queryWrapper);
                }

                // 合并 ES 和 MySQL 的结果，去重（以postId为键）
                Map<String, Post> postMap = new LinkedHashMap<>();
                for (Post post : esPosts) {
                    if (post.getPostId() != null) {
                        postMap.put(post.getPostId(), post);
                    }
                }
                for (Post post : mysqlPosts) {
                    if (post.getPostId() != null) {
                        postMap.put(post.getPostId(), post);
                    }
                }
                for(Post post : mysqlPostsFromUsername){
                    if(post.getPostId() != null){
                        postMap.put(post.getPostId(), post);
                    }
                }
                List<Post> combinedPosts = new ArrayList<>(postMap.values());

                // 这里简单分页：总数和取子列表（注意如果有排序要求，可额外处理排序）
                int total = combinedPosts.size();
                int fromIndex = Math.min((page - 1) * size, total);
                int toIndex = Math.min(fromIndex + size, total);
                List<Post> pageRecords = combinedPosts.subList(fromIndex, toIndex);

                postPage.setTotal(total);
                postPage.setRecords(pageRecords);
                return postPage;
        }
        // 2. 如果 hashtag 不为空，则按标签搜索
        else if (StrUtil.isNotEmpty(hashtag)) {
            String subQuery = "SELECT ph.post_id FROM post_hashtags ph " +
                    "JOIN hashtags h ON ph.hashtag_id = h.hashtag_id " +
                    "WHERE h.name LIKE '%" + hashtag + "%'";
            queryWrapper.inSql(Post::getPostId, subQuery);
        }

        return postMapper.selectPage(postPage, queryWrapper);
    }

    @Override
    public List<PostCategory> getPostCategories() {
        String key = RedisConstants.POST_CATEGORY_KEY;
        // 尝试从 Redis 缓存中获取分类数据，存储格式为 JSON 字符串
        String json = stringRedisTemplate.opsForValue().get(key);
        List<PostCategory> categories = null;
        if (StringUtils.hasText(json)) {
            // 使用 FastJson 将 JSON 解析为 List<PostCategory>
            categories = JSON.parseArray(json, PostCategory.class);
        } else {
            // 如果缓存中没有，则从数据库查询
            categories = postCategoryMapper.selectList(null);
            if (categories != null && !categories.isEmpty()) {
                // 将查询结果转换为 JSON 字符串，并存入 Redis，设置过期时间 1 小时
                stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(categories), Duration.ofHours(1));
            }
        }
        return categories;
    }

    @Override
    public Post getPostById(Integer postId) {
        if (postId == null) {
            return null;
        }
        String postKey = POST_KEY_PREFIX + postId;
        // 尝试从 Redis 缓存中获取帖子数据
        String poststr = stringRedisTemplate.opsForValue().get(postKey);
        Post post;
        if (StrUtil.isNotEmpty(poststr)) {
            post= JSONUtil.toBean(poststr, Post.class);
        }else {
            // 若不存在，则查询数据库
            post = postMapper.selectById(postId);
            if (post != null) {
                // 存入 Redis，设置缓存过期时间，例如1小时
                stringRedisTemplate.opsForValue().set(postKey,JSON.toJSONString(post), 1, TimeUnit.HOURS);
            }
        }

        // 更新热点帖子数据：将该帖子在 Sorted Set 中的分数 +1
        // 这里使用帖子ID作为member，分数加1（可以根据需要加权多种行为）
        stringRedisTemplate.opsForZSet().incrementScore(RedisConstants.HOT_POSTS_KEY, postId.toString(), 1);
        // 将访问记录写入 Redis Stream，同时保存用户ID
        Map<String, String> record = new HashMap<>();
        record.put("postId", postId.toString());
        record.put("increment", "1");

        // 获取当前用户ID，如果未登录返回 "anonymous"
        String currentUserId = "1"; // 暂时硬编码
        record.put("userId", (currentUserId != null ? currentUserId : "anonymous"));
        record.put("ipAddress", "127.0.0.1"); // 暂时硬编码
        record.put("createTime", String.valueOf(System.currentTimeMillis()));
        stringRedisTemplate.opsForStream().add(RedisConstants.POST_VIEW_STREAM_KEY, record);
        // 将浏览记录写入用户浏览记录集合
        String userViewedPostsKey = "user:" + currentUserId + ":viewedPosts";  // 用户的浏览记录 key
        stringRedisTemplate.opsForSet().add(userViewedPostsKey, postId.toString());  // 将当前帖子 ID 添加到用户浏览记录集合

        return post;
    }

    @Scheduled(fixedDelay = 60000)  // 每60秒执行一次
    public void syncViewCountsFromStream() {
        final String streamKey = RedisConstants.POST_VIEW_STREAM_KEY;
        final String groupName = RedisConstants.POST_VIEW_GROUP;
        final String consumerName = RedisConstants.CONSUMER_NAME;

        final long tenMinutes = 10 * 60 * 1000L; // 10分钟的毫秒数
        // 尝试创建消费者组，如果已存在则忽略异常
        try {
            stringRedisTemplate.opsForStream().createGroup(streamKey, groupName);
        } catch (Exception e) {
            // 消费者组可能已存在，忽略异常
        }

        StreamOperations<String, Object, Object> streamOps = stringRedisTemplate.opsForStream();

        // 从消费者组中读取最多100条消息，等待1秒钟
        List<MapRecord<String, Object, Object>> records = streamOps.read(
                Consumer.from(groupName, consumerName),
                StreamReadOptions.empty().count(100).block(Duration.ofSeconds(1)),
                StreamOffset.create(streamKey, ReadOffset.lastConsumed())
        );

        if (records == null || records.isEmpty()) {
            return;
        }

        // 聚合各帖子访问次数
        Map<String, Integer> aggregatedCounts = new HashMap<>();
        List<RecordId> recordIds = new ArrayList<>();

        long currentTime = System.currentTimeMillis();

        for (MapRecord<String, Object, Object> record : records) {
            // 先获取必要字段
            String postIdStr = (String) record.getValue().get("postId");
            String incrementStr = (String) record.getValue().get("increment");
            String createTimeStr = (String) record.getValue().get("createTime");

            // 如果缺少关键字段则跳过（同时记录ID，用于后续删除）
            if (postIdStr == null || incrementStr == null || createTimeStr == null) {
                recordIds.add(record.getId());
                continue;
            }
            long messageTime = Long.parseLong(createTimeStr);
            // 判断消息是否过期（超过10分钟）
            if (currentTime - messageTime > tenMinutes) {
                // 消息过期，直接标记删除，不进行统计
                recordIds.add(record.getId());
                continue;
            }

            String postId = postIdStr;
            Integer increment = Integer.valueOf(incrementStr);
            aggregatedCounts.put(postId, aggregatedCounts.getOrDefault(postId, 0) + increment);
            recordIds.add(record.getId());

            // 同时，记录详细访问日志到 post_view_log 表
            PostViewLog log = new PostViewLog();
            log.setPostId(postId);
            String userIdStr = (String) record.getValue().get("userId");
            try {
                int uid = Integer.parseInt(userIdStr);
                log.setUserId(uid);
            } catch (Exception ex) {
                log.setUserId(null);
            }
            String ipAddress = (String) record.getValue().get("ipAddress");
            log.setIpAddress(ipAddress);
            log.setViewTime(LocalDateTime.now());
            log.setExtraInfo(null);
            postViewLogMapper.insert(log);
        }

        // 更新数据库中每个帖子累积的浏览次数
        for (Map.Entry<String, Integer> entry : aggregatedCounts.entrySet()) {
            postMapper.incrementViewCount(entry.getKey(), (int) entry.getValue().longValue());
        }

        // 对已处理的消息进行ACK，并删除这些消息
        if (!recordIds.isEmpty()) {
            streamOps.acknowledge(streamKey, groupName, recordIds.toArray(new RecordId[0]));
            for (RecordId id : recordIds) {
                stringRedisTemplate.opsForStream().delete(streamKey, id);
            }
        }
    }
    @Autowired
    private PostRepository postRepository;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private TagService tagService;
    @Autowired
    private PostLikeStatMapper postLikeStatMapper;
    @Autowired
    private PostCollectStatMapper postCollectStatMapper;
    @Autowired
    private PostCommentStatMapper postCommentStatMapper;
    @Autowired
    private HashTagService hashTagService;
    @Override
    public List<Post> findByUserId(String  userId) {
        return postMapper.selectList(
                new LambdaQueryWrapper<Post>()
                        .eq(Post::getUserId, Integer.valueOf(userId))
        );
    }
    @Override
    public Result<?> createPost(PostCreateDTO dto, String userId) {
        // 校验分类、标签

        if (!categoryService.existsById(dto.getCategoryId())) {
            return Result.error("分类不存在");

        }
        if (!hashTagService.existsAllByIds(dto.getTags())) {
            return Result.error("部分标签不存在");
        }


        // 构建 Post 实体
        Post post = new Post();
        String postId = IdUtil.getSnowflake().nextIdStr(); // 生成字符串主键
        String mediaUrlsJson = JSON.toJSONString(dto.getImages());
        post.setPostId(postId);
        post.setMediaUrls(mediaUrlsJson);
        // —— 新增：如果 mediaUrls 不为空，取第一个做为 coverUrl
        if (dto.getImages() != null && !dto.getImages().isEmpty()) {
            post.setCoverUrl(dto.getImages().get(0));
        }
        post.setUserId(Integer.valueOf(userId));
        post.setTitle(dto.getTitle());
        post.setContent(dto.getContent());
        post.setCategoryId(dto.getCategoryId());
        post.setLikeCount(0L);
        post.setCollectionCount(0L);
        post.setCommentCount(0L);
        post.setViewCount(0L);
        post.setStatus(1); // 默认正常
        post.setNoteType(0); // 默认图片类型（0：图片, 1：视频），可以根据images扩展判断
        post.setPinned(0);  // 默认未置顶
        post.setCount(dto.getImages() != null ? dto.getImages().size() : 0); // 图片数量
        post.setCreatedAt(LocalDateTime.now());
        post.setUpdatedAt(LocalDateTime.now());
        postMapper.insert(post);
        //同步到 Elasticsearch
//        try {
//            IndexRequest indexRequest = new IndexRequest("posts")  // 索引名
//                    .id(postId.toString())          // 文档ID
//                    .source(JSON.toJSONString(post), XContentType.JSON);  // 内容
//
//            restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
//        } catch (Exception e) {
//            e.printStackTrace();
//            // 可以记录日志或发警告，但不要影响主业务流程
//        }
        //List<Integer>list=hashTagService.createHashtagAndReturnId(post.getContent());
        // 保存标签到 post_hashtags 表


        List<PostHashtag> postHashtags = dto.getTags().stream().map(tagId -> {
            PostHashtag ph = new PostHashtag();
            ph.setPostId(postId);
            ph.setHashtagId(tagId);
            ph.setCreatedAt(LocalDateTime.now());
            return ph;
        }).collect(Collectors.toList());
        List<HashTag> res=hashTagService.createByContent(post.getContent());
        if(res!=null){
            for(HashTag h:res){
                PostHashtag ph = new PostHashtag();
                ph.setPostId(postId);
                ph.setHashtagId(h.getHashtagId());
                ph.setCreatedAt(LocalDateTime.now());
                postHashtags.add(ph);
            }
        }
        if( !postHashtags.isEmpty()){
            postHashtagMapper.insertBatch(postHashtags);
        }
        return Result.success(post);
    }
    @Override
    public PostVO toPostVO(Post post) {
        PostVO vo = new PostVO();
        BeanUtils.copyProperties(post, vo);

        // 填充用户昵称
        User user = userMapper.findById(Long.valueOf(post.getUserId()));
        vo.setNickname(user.getNickname());
        vo.setAvatarUrl(user.getAvatarUrl());

        // 获取 hashtag 名称列表
        List<Integer> hashtagIds = postHashtagMapper.selectList(
                new QueryWrapper<PostHashtag>().eq("post_id", post.getPostId())
        ).stream().map(PostHashtag::getHashtagId).collect(Collectors.toList());

        List<String> hashtagNames = new ArrayList<>();
        if (!hashtagIds.isEmpty()) {
            hashtagNames = hashtagMapper.selectBatchIds(hashtagIds).stream()
                    .map(HashTag::getName)
                    .collect(Collectors.toList());
        }
        vo.setHashtags(hashtagNames);

        // 填充分类名（可选）
        if (post.getCategoryId() != null) {
            PostCategory category = postCategoryMapper.selectById(post.getCategoryId());
            if (category != null) {
                vo.setCategoryName(category.getName());
            }
        }

        return vo;
    }

    @Override
    public String createPostByAI(PostCreateDTO dto,  String userId) {
        log.info("调用发帖逻辑");
        if (!categoryService.existsById(dto.getCategoryId())) {
            return "分类不存在，发帖失败";

        }
        if (!hashTagService.existsAllByIds(dto.getTags())) {
            return "部分标签不存在，发帖失败";
        }


        // 构建 Post 实体
        Post post = new Post();
        String postId = IdUtil.getSnowflake().nextIdStr(); // 生成字符串主键
        String mediaUrlsJson = JSON.toJSONString(dto.getImages());
        post.setPostId(postId);
        post.setMediaUrls(mediaUrlsJson);
        // —— 新增：如果 mediaUrls 不为空，取第一个做为 coverUrl
        if (dto.getImages() != null && !dto.getImages().isEmpty()) {
            post.setCoverUrl(dto.getImages().get(0));
        }
        post.setUserId(Integer.valueOf(userId));
        post.setTitle(dto.getTitle());
        post.setContent(dto.getContent());
        post.setCategoryId(dto.getCategoryId());
        post.setLikeCount(0L);
        post.setCollectionCount(0L);
        post.setCommentCount(0L);
        post.setViewCount(0L);
        post.setStatus(1); // 默认正常
        post.setNoteType(0); // 默认图片类型（0：图片, 1：视频），可以根据images扩展判断
        post.setPinned(0);  // 默认未置顶
        post.setCount(dto.getImages() != null ? dto.getImages().size() : 0); // 图片数量
        post.setCreatedAt(LocalDateTime.now());
        post.setUpdatedAt(LocalDateTime.now());
        postMapper.insert(post);
        //同步到 Elasticsearch
//        try {
//            IndexRequest indexRequest = new IndexRequest("posts")  // 索引名
//                    .id(postId.toString())          // 文档ID
//                    .source(JSON.toJSONString(post), XContentType.JSON);  // 内容
//
//            restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
//        } catch (Exception e) {
//            e.printStackTrace();
//            // 可以记录日志或发警告，但不要影响主业务流程
//        }
        // 保存标签到 post_hashtags 表
        List<PostHashtag> postHashtags = dto.getTags().stream().map(tagId -> {
            PostHashtag ph = new PostHashtag();
            ph.setPostId(postId);
            ph.setHashtagId(tagId);
            ph.setCreatedAt(LocalDateTime.now());
            return ph;
        }).collect(Collectors.toList());
        if( !postHashtags.isEmpty()){
            postHashtagMapper.insertBatch(postHashtags);
        }
        log.info("发帖成功了");
        return "发帖成功了,帖子标题"+ post.getTitle();
    }


    @Override
    public Result<?> updatePost(String  postId, PostCreateDTO  dto, String userId) {
        // 1. 先查询旧帖子
        Post old = postMapper.selectById(Integer.valueOf(postId));
        if (old == null) {
            return Result.error("帖子不存在 " );

        }
        // 2. 校验权限: 只能本人或管理员编辑
        if (!old.getUserId().toString().equals(userId)) {
            return Result.error("无权编辑该帖子" );

        }
        // 3. 校验分类、标签
        if (!categoryService.existsById(dto.getCategoryId())) {
            return Result.error("分类不存在" );
        }
        if (!hashTagService.existsAllByIds(dto.getTags())) {
            return Result.error("部分标签不存在" );
        }

        // 4. 更新字段
        old.setTitle(dto.getTitle());
        old.setContent(dto.getContent());
        old.setCategoryId(dto.getCategoryId());
        old.setMediaUrls(JSON.toJSONString(dto.getImages()));
        old.setCount(dto.getImages() != null ? dto.getImages().size() : 0);
        old.setUpdatedAt(LocalDateTime.now());
        postMapper.updateById(old);
        return Result.success(old);
    }

    @Override
    public Post getPostById(String id) {
        String postKey = POST_KEY_PREFIX + id;
        // 尝试从 Redis 缓存中获取帖子数据
        String poststr = stringRedisTemplate.opsForValue().get(postKey);
        Post post;
        if (StrUtil.isNotEmpty(poststr)) {
            post= JSONUtil.toBean(poststr, Post.class);
        }else {
            // 若不存在，则查询数据库
            post = postMapper.selectById(id);
            if (post != null) {
                // 存入 Redis，设置缓存过期时间，例如1小时
                stringRedisTemplate.opsForValue().set(postKey,JSON.toJSONString(post), 1, TimeUnit.HOURS);
            }
        }

        // 更新热点帖子数据：将该帖子在 Sorted Set 中的分数 +1
        // 这里使用帖子ID作为member，分数加1（可以根据需要加权多种行为）
        stringRedisTemplate.opsForZSet().incrementScore(RedisConstants.HOT_POSTS_KEY, id, 1);
        // 将访问记录写入 Redis Stream，同时保存用户ID
        Map<String, String> record = new HashMap<>();
        record.put("postId", id);
        record.put("increment", "1");
        // 获取当前用户ID，如果未登录返回 "anonymous"
        UserRedisDTO userRedisDTO = UserHolder.getUser();
        if(userRedisDTO==null){
            throw  new RuntimeException("用户未登录");
        }
        String currentUserId=String.valueOf(userRedisDTO.getUserId());
        record.put("userId", (currentUserId != null ? currentUserId : "anonymous"));
        record.put("ipAddress", "127.0.0.1"); // 暂时硬编码
        record.put("createTime", String.valueOf(System.currentTimeMillis()));
        stringRedisTemplate.opsForStream().add(RedisConstants.POST_VIEW_STREAM_KEY, record);
        // 将浏览记录写入用户浏览记录集合
        String userViewedPostsKey = "user:" + currentUserId + ":viewedPosts";  // 用户的浏览记录 key
        stringRedisTemplate.opsForSet().add(userViewedPostsKey, id);  // 将当前帖子 ID 添加到用户浏览记录集合

        if (post == null || post.getPostId() == null) {
            return null;
        }

        // 加载交互数据
        PostLikeStat likeStat = postLikeStatMapper.selectByPrimaryKey(String.valueOf(post.getPostId()));
        if (likeStat != null && likeStat.getLikeCount() != null) {
            post.setLikeCount(Long.valueOf(likeStat.getLikeCount()));
        }

        PostCollectStat collectStat = postCollectStatMapper.selectByPrimaryKey(String.valueOf(post.getPostId()));
        if (collectStat != null && collectStat.getCollectCount() != null) {
            post.setCollectionCount(Long.valueOf(collectStat.getCollectCount()));
        }

        PostCommentStat commentStat = postCommentStatMapper.selectByPrimaryKey(String.valueOf(post.getPostId()));
        if (commentStat != null && commentStat.getCommentCount() != null) {
            post.setCommentCount(Long.valueOf(commentStat.getCommentCount()));
        }

        return post;
    }

    @Override
    public Post save(Post post) {
        if (post.getPostId() == null) {
            postMapper.insert(post);
        } else {
            postMapper.updateById(post);
        }
        return post;
    }

    @Override
    public Post findById(String id) {
        return postMapper.selectById(Integer.valueOf(id));
    }

    @Override
    public void deleteById(String id) {
        //TODO:多表关联的删除
        postMapper.deleteById(Integer.valueOf(id));
    }

    @Override
    public Post update(Post post) {
        postMapper.updateById(post);
        return post;
    }

    @Override
    public List<Post> findAll() {

        return postMapper.selectList(null);
    }

    @Override
    public int getLikeCount(String postId){
        return postMapper.selectLikeCountInt(postId);
    };
    @Override
    public int getCommentCount(String postId){
        return postMapper.selectCommentCount(postId);
    };
    @Override
    public int getCollectionCount(String postId) {
        return postMapper.selectFavoriteCount(postId);
    }

}




